﻿using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sustainalytics.ClientDW.Entities.Universes;
using Sustainalytics.Entities;
using Sustainalytics.Entities.ESGRatings;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using AccountFeatureEnum = Sustainalytics.Auth.Claims.AccountFeature;

namespace Sustainalytics.DynamicsCRMGateway.Tests
{
    [TestClass]
    public class UniversesCrmReaderTests
    {
        private const string ConnectionString = "Url=https://sustainalytics0.crm4.dynamics.com;Username=client.platform@sustainalytics.com;Password=M46iaHerm3s21!";
        private const int NextUniverseId = 1;

        private CrmTestDataHelper _crmTestDataHelper;
        private UniversesCrmReader _universesCrmReader;

        [TestInitialize]
        public void Initialize()
        {
            var crmServiceContextFactory = new CrmServiceContextFactory();
            var crmServiceContext = crmServiceContextFactory.GetCrmServiceContext(ConnectionString);

            _crmTestDataHelper = new CrmTestDataHelper(crmServiceContext);
            _universesCrmReader = new UniversesCrmReader(crmServiceContext);

            AssertionOptions.AssertEquivalencyUsing(options => options.Excluding(x => x.CompileTimeType == typeof(DateTime)));
        }

        [TestCleanup]
        public void Cleanup()
        {
            _crmTestDataHelper.Delete();
        }

        [TestMethod]
        [Ignore]
        public void ShouldReturnUniverses()
        {
            _crmTestDataHelper
                .AddAccountWithProductUniverse(1, 1)
                .AddAccountWithProductUniverse(1, 2)
                .AddAccountWithProductUniverse(1, 3)
                .AddAccountWithProductUniverse(2, 2)
                .AddAccountWithProductUniverse(2, 3)
                .AddAccountWithProductUniverse(3, 3)
                .Create();

            var nextUniverseId = NextUniverseId;
            var expectedUniverses = new[]
            {
                GetExpectedUniverse(nextUniverseId++, 1, 1),
                GetExpectedUniverse(nextUniverseId++, 1, 2),
                GetExpectedUniverse(nextUniverseId++, 1, 3),
                GetExpectedUniverse(nextUniverseId++, 2, 2),
                GetExpectedUniverse(nextUniverseId++, 2, 3),
                GetExpectedUniverse(nextUniverseId++, 3, 3),
            };

            var universes = GetUniverses();

            universes.Should().BeEquivalentTo(expectedUniverses);
        }

        [TestMethod]
        [Ignore]
        public void ShouldReturnFeatureUniverses()
        {
            _crmTestDataHelper
                .AddAccountWithFeatureAndUniverses(1, 1, new[] { 1, 2, 3 })
                .AddAccountWithFeatureAndUniverses(1, 2, new[] { 1, 4 })
                .AddAccountWithFeatureAndUniverses(1, 3, new[] { 1, 4 })
                .AddAccountWithFeatureAndUniverses(2, 3, new[] { 5, 6, 7 })
                .AddAccountWithFeatureAndUniverses(3, 2, new[] { 8, 9 })
                .AddAccountWithFeatureAndUniverses(3, 4, new[] { 10 })
                .Create();

            var expectedFeatureUniverses = new[]
            {
                GetExpectedFeatureUniverse(1, 1, new[] { 1, 2, 3 }),
                GetExpectedFeatureUniverse(1, 2, new[] { 1, 4 }),
                GetExpectedFeatureUniverse(1, 3, new[] { 1, 4 }),
                GetExpectedFeatureUniverse(2, 3, new[] { 5, 6, 7 }),
                GetExpectedFeatureUniverse(3, 2, new[] { 8, 9 }),
                GetExpectedFeatureUniverse(3, 4, new[] { 10 })
            };

            var featureUniverses = _universesCrmReader.GetFeatureUniverses(GetUniverses())
                .WhereAccountInTestAccounts(_crmTestDataHelper);

            featureUniverses.Should().BeEquivalentTo(expectedFeatureUniverses, config => config.Excluding(x => x.Id));
        }

        [TestMethod]
        [Ignore]
        public void ShouldReturnStandardUniverseCompanies()
        {
            var referenceUniverses = new[]
            {
                GetReferenceUniverse(1, new[] { 1, 2, 3 }),
                GetReferenceUniverse(2, new[] { 1, 3 }),
                GetReferenceUniverse(3, new[] { 1, 2 }),
                GetReferenceUniverse(4, new[] { 4 }),
            };

            _crmTestDataHelper
                .AddAccountWithUniverseAndCompaniesGroups(1, 1, new[] { 1, 2, 3 })
                .AddAccountWithUniverseAndCompaniesGroups(1, 2, new[] { 4 })
                .AddAccountWithUniverseAndCompaniesGroups(2, 3, new[] { 2, 4 })
                .AddAccountWithUniverseAndCompaniesGroups(3, 4, new[] { 2, 3 })
                .AddAccountWithUniverseAndCompaniesGroups(3, 5, new[] { 3 })
                .Create();

            var expectedStandardUniverseCompanies = new[]
            {
                GetExpectedUniverseCompany(1, 1, 1),
                GetExpectedUniverseCompany(1, 1, 2),
                GetExpectedUniverseCompany(1, 1, 3),
                GetExpectedUniverseCompany(1, 2, 4),
                GetExpectedUniverseCompany(2, 3, 1),
                GetExpectedUniverseCompany(2, 3, 3),
                GetExpectedUniverseCompany(2, 3, 4),
                GetExpectedUniverseCompany(3, 4, 1),
                GetExpectedUniverseCompany(3, 4, 2),
                GetExpectedUniverseCompany(3, 4, 3),
                GetExpectedUniverseCompany(3, 5, 1),
                GetExpectedUniverseCompany(3, 5, 2),
            };

            var standardUniverseCompanies = _universesCrmReader.GetStandardUniverseCompanies(GetUniverses(), referenceUniverses)
                .WhereAccountInTestAccounts(_crmTestDataHelper);

            standardUniverseCompanies.Should().BeEquivalentTo(expectedStandardUniverseCompanies, config => config.Excluding(x => x.Id));
        }

        [TestMethod]
        [Ignore]
        public void ShouldReturnCustomUniverseCompanies()
        {
            _crmTestDataHelper
                .AddCompaniesGroupWithCompanies(1, new[] { 1, 2, 3 })
                .AddCompaniesGroupWithCompanies(2, new[] { 1, 3 })
                .AddCompaniesGroupWithCompanies(3, new[] { 1, 2 })
                .AddCompaniesGroupWithCompanies(4, new[] { 4 })
                .AddAccountWithUniverseAndCompaniesGroups(1, 1, new[] { 1, 2, 3 })
                .AddAccountWithUniverseAndCompaniesGroups(1, 2, new[] { 4 })
                .AddAccountWithUniverseAndCompaniesGroups(2, 3, new[] { 2, 4 })
                .AddAccountWithUniverseAndCompaniesGroups(3, 4, new[] { 2, 3 })
                .AddAccountWithUniverseAndCompaniesGroups(3, 5, new[] { 3 })
                .Create();

            var expectedCustomUniverseCompanies = new[]
            {
                GetExpectedUniverseCompany(1, 1, 1),
                GetExpectedUniverseCompany(1, 1, 2),
                GetExpectedUniverseCompany(1, 1, 3),
                GetExpectedUniverseCompany(1, 2, 4),
                GetExpectedUniverseCompany(2, 3, 1),
                GetExpectedUniverseCompany(2, 3, 3),
                GetExpectedUniverseCompany(2, 3, 4),
                GetExpectedUniverseCompany(3, 4, 1),
                GetExpectedUniverseCompany(3, 4, 2),
                GetExpectedUniverseCompany(3, 4, 3),
                GetExpectedUniverseCompany(3, 5, 1),
                GetExpectedUniverseCompany(3, 5, 2),
            };

            var customUniverseCompanies = _universesCrmReader.GetCustomUniverseCompanies(GetUniverses())
                .WhereAccountInTestAccounts(_crmTestDataHelper);

            customUniverseCompanies.Should().BeEquivalentTo(expectedCustomUniverseCompanies, config => config.Excluding(x => x.Id));
        }

        private Universe GetExpectedUniverse(int id, int account, int universe)
        {
            return new Universe()
            {
                Id = id,
                AccountId = _crmTestDataHelper.Accounts[account].Id,
                Name = CrmTestDataHelper.GetTestName(universe)
            };
        }

        private FeatureUniverse GetExpectedFeatureUniverse(int account, int feature, IEnumerable<int> universeIds)
        {
            return new FeatureUniverse()
            {
                AccountId = _crmTestDataHelper.Accounts[account].Id,
                Feature = (AccountFeatureEnum)feature,
                UniverseIds = universeIds.ToList()
            };
        }

        private UniverseCompany GetExpectedUniverseCompany(int account, int universeId, int company)
        {
            return new UniverseCompany()
            {
                CompanyId = _crmTestDataHelper.Companies.Any() ? _crmTestDataHelper.Companies[company].Id : GetGuid(company),
                AccountId = _crmTestDataHelper.Accounts[account].Id,
                CIQID = CrmTestDataHelper.GetTestName(company),
                UniverseId = universeId
            };
        }

        private ReferenceUniverse GetReferenceUniverse(int referenceUniverse, IEnumerable<int> companies)
        {
            return new ReferenceUniverse()
            {
                Name = CrmTestDataHelper.GetTestName(referenceUniverse),
                Companies = companies.Select(company => new ReferenceUniverseCompany()
                {
                    CompanyId = GetGuid(company),
                    CapitalIqId = new Random().Next(1, 999999),
                    CompanyName = CrmTestDataHelper.GetTestName(company)
                }).ToList()
            };
        }

        private IEnumerable<Universe> GetUniverses()
        {
            return _universesCrmReader.GetUniverses(NextUniverseId).WhereAccountInTestAccounts(_crmTestDataHelper);
        }

        private static Guid GetGuid(int input)
        {
            using (MD5 md5 = MD5.Create())
            {
                byte[] hash = md5.ComputeHash(Encoding.Default.GetBytes(input.ToString()));
                return new Guid(hash);
            }
        }
    }

    public static class IEnumerableExtensions
    {
        public static IEnumerable<T> WhereAccountInTestAccounts<T>(this IEnumerable<T> data, CrmTestDataHelper crmTestDataHelper)
            where T : IAccountRelatedEntity
        {
            return data.Where(x => crmTestDataHelper.Accounts.Values.Any(account => x.AccountId == account.AccountId)).ToList();
        }
    }
}
